﻿
Imports std

Namespace tablestore
    Public Module tablestore
        Public Const lineCountInEachKey As Int64 = 1024

        Private Class column
            Inherits columns

            Public Function full() As Boolean
                Return size() >= lineCountInEachKey
            End Function

            Protected Overrides Function enableWrite() As Boolean
                Return False
            End Function
        End Class

        Private Class columns
            Inherits list

            Protected Overrides Function enableModify() As Boolean
                Return True
            End Function

            Protected Overrides Function enableRead() As Boolean
                Return True
            End Function

            Protected Overrides Function enableUniqueWrite() As Boolean
                Return False
            End Function

            Protected Overrides Function enableWrite() As Boolean
                Return True
            End Function

            Protected Overrides Function keyprefix() As String
                Return Nothing
            End Function
        End Class

        Public Const columnsKeySuffix As String = "$COLUMNS$"
        Public Const columnLengthKeySuffix As String = character.dollar

        Private Function columnsKey(ByVal tableName As String) As String
            Return generatekey(tableName, columnsKeySuffix)
        End Function

        Private Function columnKey(ByVal tableName As String, ByVal columnName As String) As String
            Return generatekey(tableName, columnName)
        End Function

        Private Function columnKey(ByVal tableName As String, ByVal columnName As String, _
                                   ByVal index As Int64) As String
            Return generatekey(columnKey(tableName, columnName), index)
        End Function

        Private Function lengthKey(ByVal tableName As String, ByVal columnName As String) As String
            Return generatekey(columnKey(tableName, columnName), columnLengthKeySuffix)
        End Function

        Private Function updateColumns(ByVal tableName As String, ByVal columnName As String, _
                                       ByVal talk As storoomTalk) As Boolean
            Dim cmsk As String = Nothing
            cmsk = columnsKey(tableName)
            If locks.lock(cmsk, talk) Then
                Dim cms As columns = Nothing
                cms = New columns()
                Dim rtn As Boolean
                cms.push_back(columnName)
                rtn = cms.append(cmsk, talk)
                locks.release(cmsk, talk)
                Return rtn
            Else
                Return False
            End If
        End Function

        Private Function writeToExisting(ByVal columnKey As String, ByVal data As String, _
                                         ByVal talk As storoomTalk, Optional ByVal cm As column = Nothing) As Boolean
            If cm Is Nothing Then
                cm = New column()
            End If
            cm.push_back(data)
            Return cm.modify(columnKey, talk)
        End Function

        Private Function writeToNew(ByVal tableName As String, ByVal columnName As String, ByVal data As String, _
                                    ByVal length As Int64, ByVal talk As storoomTalk) As Boolean
            Static sizeplus As bytes = New bytes(Convert.ToByte("H"c))
            Return writeToExisting(columnKey(tableName, columnName, length), data, talk) AndAlso _
                   talk.write(lengthKey(tableName, columnName), sizeplus)
        End Function

        Private Function writeImpl(ByVal tableName As String, ByVal columnName As String, ByVal data As String, _
                                   ByVal talk As storoomTalk) As Boolean
            If Not updateColumns(tableName, columnName, talk) Then
                Return False
            Else
                Dim length As Int64 = 0
                Dim lkey As String = Nothing
                lkey = lengthKey(tableName, columnName)
                If talk.sizeof(lkey, length) AndAlso length > 0 Then
                    Dim cmk As String = Nothing
                    cmk = columnKey(tableName, columnName, length - 1)
                    Dim cm As column = Nothing
                    cm = New column()
                    If cm.read(cmk, talk) Then
                        If cm.full() Then
                            Return writeToNew(tableName, columnName, data, length, talk)
                        Else
                            Return writeToExisting(cmk, data, talk, cm)
                        End If
                    Else
                        raiseError(errorHandle.errorType.warning, "cannot read ", cmk, ", assume empty.")
                        cm.clear()
                        Return writeToExisting(cmk, data, talk, cm)
                    End If
                Else
                    Return writeToNew(tableName, columnName, data, 0, talk)
                End If
            End If
        End Function

        Public Function write(ByVal tableName As String, ByVal columnName As String, ByVal data As String, _
                              Optional ByVal talk As storoomTalk = Nothing) As Boolean
            normalizeTalk(talk)
            Dim cmk As String = Nothing
            cmk = columnKey(tableName, columnName)
            Try
                If locks.lock(cmk, talk) Then
                    Dim rtn As Boolean
                    rtn = writeImpl(tableName, columnName, data, talk)
                    locks.release(cmk, talk)
                    Return rtn
                Else
                    Return False
                End If
            Catch ex As Exception
                locks.release(cmk, talk)
                If Not TypeOf ex Is Threading.ThreadAbortException Then
                    raiseError(errorHandle.errorType.critical, _
                               "unhandled exception caught when tablestore.write, ex ", ex.Message, _
                               ", callstack ", ex.StackTrace)
                End If
                Return False
            End Try
        End Function

        Public Function read(ByVal tableName As String, ByVal columnName As String, ByRef data As vector(Of String), _
                             Optional ByVal talk As storoomTalk = Nothing) As Boolean
            Dim length As Int64 = 0
            If talk.sizeof(lengthKey(tableName, columnName), length) Then
                If data Is Nothing Then
                    data = New vector(Of String)()
                Else
                    data.clear()
                End If
                For i As Int64 = 0 To length - 1
                    Dim cmk As String = Nothing
                    cmk = columnKey(tableName, columnName, i)
                    Dim cm As column = Nothing
                    cm = New column()
                    If cm.read(cmk, talk) Then
                        If Not cm.full() AndAlso i < length - 1 Then
                            raiseError(errorHandle.errorType.warning, "column in table is not full, key ", cmk)
                        End If
                        Dim j As column.iterator = Nothing
                        j = cm.begin()
                        While j <> cm.end()
                            data.push_back(+j)
                            j += 1
                        End While
                    Else
                        raiseError(errorHandle.errorType.warning, "cannot read ", cmk, ", assume empty.")
                    End If
                Next
                Return True
            Else
                Return False
            End If
        End Function

        Public Function readColumns(ByVal tableName As String, ByRef columns As vector(Of String), _
                                    Optional ByVal talk As storoomTalk = Nothing) As Boolean
            normalizeTalk(talk)
            Dim cmsk As String = Nothing
            cmsk = columnsKey(tableName)
            Dim cms As columns = Nothing
            cms = New columns()
            If cms.read(cmsk, talk) Then
                Return list2vector(cms, columns)
            Else
                Return False
            End If
        End Function
    End Module
End Namespace
